移动端H5开发之页面适配篇 |
您所在的位置:网站首页 › html5 手机适配 › 移动端H5开发之页面适配篇 |
最近开发并上线了一款H5项目,在这里想和大家分享一下关于项目中使用到的移动端适配技巧,如果对你们有所帮助的话,就多多点赞收藏😃 各位看官老爷别着急,在讲页面适配之前,我们先来捋一捋viewport(视口)的概念~ 在Web浏览器术语中,通常与浏览器窗口相同,但不包括浏览器的UI, 菜单栏等——即指你正在浏览的文档的那一部分。 一般我们所说的视口共包括三种:布局视口、视觉视口和理想视口 1.1 布局视口![]() 在移动端,布局视口被赋予一个默认值,大部分为980px,这保证PC的网页可以在手机浏览器上呈现,用户可以手动对网页进行放大。 我们可以通过调用 document.documentElement.clientWidth / clientHeight来获取布局视口大小。 1.2 视觉视口![]() 视觉视口,用户通过屏幕真实看到的区域。 我们可以通过调用 window.innerWidth / innerHeight 来获取视觉视口大小。 1.3 理想视口![]() 视觉视口,用户通过屏幕真实看到的区域 我们可以通过调用 window.screen.width / height 来获取视觉视口大小 1.4 页面适配方法综上所述,为了在移动端让页面获得更好的显示效果,我们必须让布局视口、视觉视口都尽可能等于理想视口。 我们可以借助元素的viewport来帮助我们设置视口、缩放等 device-width就等于理想视口的宽度,所以设置width=device-width就相当于让布局视口等于理想视口。 由于initial-scale = 理想视口宽度 / 视觉视口宽度,所以我们设置initial-scale=1;就相当于让视觉视口等于理想视口。 这样我们就实现了布局视口 = 理想视口。下面附上完整的viewport取值介绍。 ![]() 通过上文了解到,viewport有个initial-scale属性,用来定义页面初始缩放比率,我们是否可以通过动态的改变这个缩放值来进行适配呢,答案是可以的! function viewPort(){ const meta = document.querySelector('meta[name="viewport"]'); const scale = window.screen.width / 750 > 1 ? 1 : window.screen.width / 750; meta.content = `width=device-width,initial-scale=${scale}` } viewPort() window.onresize = viewPort;通过这段代码就实现了适配,我们来分析一下: 我们设置UI提供的设计稿为750,当然也可以是别的大小,假定我们就根据750px的UI图来写css,当用户的设备理想视图 window.screen.width 大于750时,我们就把页面整体缩放,如果小于750时,就把页面整体放大,缩小和放大的比例,我们通过 window.screen.width / 750 (设计稿大小) 来获取,然后动态的设置到meta标签上。 下图为实现效果 ![]() 这种方式虽然能够解决适配问题,但也会过于简单粗暴,主要有以下两个问题 1. 全局缩放,把不需要缩放的也影响了; 2. 如果有第三方UI库,会影响了第三方库的显示效果; 1.4.2 通过rem来适配rem(font size of the root element)是指相对于根元素的字体大小的单位。简单的说它就是一个相对单位。看到rem大家一定会想起em单位,em(font size of the element)是指相对于父元素的字体大小的单位。它们之间其实很相似,只不过一个计算的规则是依赖根元素一个是依赖父元素计算。 下图所示,如果根元素html的字体大小为100px的话,1rem也就等于100px。 html{ font-size: 100px; } #root { font-size: 1rem; /* 100px */ }基于这个原理,可以推导出,如果设计稿宽是750px,且以设计图为标准的 html标签的font-size为100px,那么这个设计图总宽就有 7.5rem,如果动态的更改根元素的字体大小,是不是就可以实现动态改变元素的大小了?答案是的! 实现这个只要简单的3步即可,首先设置viewport为理想窗口,并且初始化缩放为0 然后书写js逻辑,监听页面resize事件,动态的设置html的字体大小 (function (window, html) { // 规定默认的设计稿宽度720px const designWidth = 720; function recalc() { const windowWidth = html.clientWidth < designWidth ? html.clientWidth : designWidth; // *100 之后,则样式中rem的值就需要相应的缩小100倍 // 即:设计稿中的20px,在样式中就要写成0.2rem const fontSize = windowWidth / designWidth * 100; setFontSize(fontSize); } function setFontSize(fontSize) { html.style.fontSize = `${fontSize}px`; } // 监听resize window.addEventListener('resize', recalc); recalc(); }(window, document.documentElement));上述代码中,核心代码const fontSize = windowWidth / designWidth * 100;规定1rem等于设计稿的宽度,也就是720px,最后乘100是为了写rem时方便计算 比如720px设计稿上的某个元素宽度为50px,那我在写rem单位时可以直接除以100写成0.5rem即可,方便计算。 下图为实现效果 ![]() 上面的代码会有一个最大变化的阀值,为designWidth也就是720px,当浏览器窗口的大小超过这个值时,就不再动态变化了,这个可以保证在pc上也能正常显示。 当然,作为一名合格的前端,怎么可以自己计算写rem呢,我们可以借助使用postcss-pxtorem插件来自动完成这项工作。 在你的项目webpack配置文件中,针对less文件增加postcss-loader { test: /\.less$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader', }, { loader: 'postcss-loader',// 增加postcss-loader }, { loader: 'less-loader', options: { javascriptEnabled: true, }, }, ], },然后在根目录中创建postcss.config.js文件 module.exports = { plugins: [ require('postcss-pxtorem')({ // 750宽度的设计稿 rootValue: 100, unitPrecision: 5, propList: ['*', '!letter-spacing'], selectorBlackList: [], replace: true, mediaQuery: false, minPixelValue: 1, exclude: /node_modules/i, }), ], };这里可以设置转换的比率、类名、小数点数量等,具体的配置可以参考配置项 如果不想被转换的话,px可以写成大写,这样就不会进行转换了 // `Px` or `PX` is ignored by `postcss-pxtorem` but still accepted by browsers .ignore { border: 1Px solid; // ignored border-width: 2PX; // ignored }这样就实现了px单位向rem单位的自由转换,就不需要一个个的计算手写了。 1.4.3 flexible1.0方案既然说到了rem的适配方案,那我们就来聊一下淘宝团队早期的flexible方案。 淘宝的手淘团队,在做移动端适配时,使用的flexible方案核心就是rem适配,打开他们的github源码,会发现比rem逻辑多了一些dpr的处理。 ![]() 所以他们的方案是rem+dpr,既然说到了这里,我们再来讲一下dpr是个什么东西? 1.4.3.1 dpr是什么dpr全名叫device pixel ratio,是设备上物理像素和设备独立像素的比率,公式表示就是:window.devicePixelRatio = 物理像素 /设备独立像素 此值也可以解释为像素大小的比率:一个CSS像素的大小与一个物理像素的大小。 简单来说,它告诉浏览器应使用多少屏幕实际像素来绘制单个CSS像素。 这个最早还是乔布斯提出来的,之前一个像素被一个物理像素渲染,但是iphone4出来之后,一个像素被两个物理像素渲染 如下图所示,左边为iphone4之前设备处理像素的逻辑,右边为iphone4处理的逻辑,可以看到一个像素被两个物理像素渲染,这样会让显示更加清楚,没有像素感。 ![]() ![]() 所以一倍的图片,在dpr为2的设备上会显示小一倍,然后flexible会针对这种情况整体缩放0.5,也就使图片正常显示。 但是上文1.4.1中也说了,直接改变initial-scale的值,会产生一些问题,所以后来flexible2.0也放弃了计算dpr这部分逻辑。 ![]() 在说viewport方案之前,我们先来解决移动端dpr普遍>=2的问题。 1.4.3.2 解决dpr带来的问题上文中讲的dpr带来的问题主要有两个:1px问题和图片模糊的问题,我们先来看看如何解决1px的问题 ![]() 这是一些目前主流的解决1px问题的方案,但是看现在的淘宝、京东等一些网站,对于1px也没有进行任何处理,估计是解决这类问题的成本/收益大于1。 再来看下图片模糊的问题,大多发生在高dpr的设备上使用低倍图场景,所以图片我们直接使用2倍图即可,因为大部分的设备dpr为2,对于一些dpr>=3的特殊设备,我们也可以使用媒体查询来适配即可 .bg { background-image: url('[email protected]); @media (-webkit-min-device-pixel-ratio: 3), (min-device-pixel-ratio: 3) { background-image: url('[email protected]'); } }1.4.4 viewport方案viewport方案就是使用css3的计算单位vw、vh等来进行适配,关于这种css3单位的官方解释点这里查看。 下图形象的解释了这个单位的定义: ![]() 再来看一下浏览器的兼容 ![]() 可以看到对于现代浏览器,这种单位已经兼容。 我们根据上面关于vw的定义,以及rem方案的了解,如果设计稿为750px,那么我们把屏幕平均分成750份,所以1px就等于 100vw / 750的vw,举个例子,如果设计稿上的某个元素为50px,那么对于的vw为 50 * 100vw / 750。 我们就得到了px和vw的转换公式:px * 100vw / 750 在项目中具体的实现方式,首先和rem一样也需要设置meta标签 然后直接写css #root { width: 53.33vw;/* 400px -> 400 * 100vw / 750 */ height: 6.66vw;/* 50px -> 50 * 100vw / 750*/ margin: auto; background-color: red; } >实现效果: ![]() 所以这种适配方式的好处是我们只写css就可以了,不需要再写额外的逻辑,但是每次手动计算确实很麻烦,我们同样可以借助自动化工具postcss-px-to-viewport来实现,具体的实现方式类似上文中rem转px,对于不想转换的,可以增加ignore注释来跳过。 /* example input: */ .class { /* px-to-viewport-ignore-next */ width: 10px; padding: 10px; height: 10px; /* px-to-viewport-ignore */ border: solid 2px #000; /* px-to-viewport-ignore */ } /* example output: */ .class { width: 10px; padding: 3.125vw; height: 10px; border: solid 2px #000; }但是viewport的适配方案也有一定的缺点,那就是不能设置一个最大宽度的阀值,只能跟着浏览器视图大小的改变而变化,这样对于一些想要在pc和h5都要正常展示的项目不太友好 1.4.5 针对刘海屏的兼容针对iphoneX以上具有刘海屏的机型,也有对应的适配方案,那就是viewport-fit 和Safe Area,先来看一下viewport-fit contain: 可视窗口完全包含网页内容 cover:网页内容完全覆盖可视窗口 默认情况下或者设置为auto和contain效果相同。 下图中,左边为contain,右边为cover ![]() Safe Area是iphoneX之后引入的新概念,指的是一个可视窗口范围,下图可以看到相关区域的定义 ![]() constant(safe-area-inset-top):在Viewport顶部的安全区域内设置量(CSS像素) constant(safe-area-inset-bottom):在Viewport底部的安全区域内设置量(CSS像素) constant(safe-area-inset-left):在Viewport左边的安全区域内设置量(CSS像素) constant(safe-area-inset-right):在Viewport右边的安全区域内设置量(CSS像素) 然后给body设置一下安全区域 body { padding: constant(safe-area-inset-top) constant(safe-area-inset-right) constant(safe-area-inset-bottom) constant(safe-area-inset-left); /* 兼容 iOS < 11.2 */ padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); /* 兼容 iOS >= 11.2 */ }再来说一下env这个css函数:env()函数以类似于var函数的方式将用户代理定义的环境变量值插入到你的 CSS 中去。这个函数最初由 iOS 浏览器提供,用于允许开发人员将其内容放置在视口的安全区域中,该规范中定义的 safe-area-inset-* 值用于确保内容即使在非矩形的视区中也可以完全显示。 但是对于ios < 11.2的系统来说,需要使用constant函数来替代env进行兼容。 通过设置viewport-fit 和 安全区域,就能完美对刘海屏进行适配了。 1.5 总结说了这么多,我们简单的来总结一下: 1.对于需要移动端、PC端都正常展示的项目推荐使用rem布局; 2.对于只在移动端展示,且内容量较少的页面推荐使用vw布局; 当然媒体查询@media也可以用来进行页面适配,但是相对于我讲的这几种显得不够灵活,针对各种条件来写css也显得不太友好,可以作为一种辅助方法来填补以上几种方法覆盖不了的地方。 下一篇会将移动端调试方法😃 参考文档: https://www.w3.org/TR/css-round-display-1/#extending-viewport-rule https://cloud.tencent.com/developer/article/1637066 https://juejin.cn/post/6844903951012200456#heading-9 https://juejin.cn/post/6953091677838344199 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |